]> git.neil.brown.name Git - wiggle.git/blob - wiggle.c
712e7fe4bbe0ee3b062fc9b0ed14a99b7c1d8daa
[wiggle.git] / wiggle.c
1 /*
2  * wiggle - apply rejected patches
3  *
4  * Copyright (C) 2003 Neil Brown <neilb@cse.unsw.edu.au>
5  * Copyright (C) 2010-2013 Neil Brown <neilb@suse.de>
6  *
7  *
8  *    This program is free software; you can redistribute it and/or modify
9  *    it under the terms of the GNU General Public License as published by
10  *    the Free Software Foundation; either version 2 of the License, or
11  *    (at your option) any later version.
12  *
13  *    This program is distributed in the hope that it will be useful,
14  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *    GNU General Public License for more details.
17  *
18  *    You should have received a copy of the GNU General Public License
19  *    along with this program.
20  *
21  *    Author: Neil Brown
22  *    Email: <neilb@suse.de>
23  */
24
25 /*
26  * Wiggle is a tool for working with patches that don't quite apply properly.
27  * It provides functionality similar to 'diff' and 'merge' but can
28  * work at the level of individual words thus allowing the merging of
29  * two changes that affect the same line, but not the same parts of that line.
30  *
31  * Wiggle can also read patch and merge files.  Unlike 'merge' it does not
32  * need to be given three separate files, but can be given a file and a patch
33  * and it will extract the pieces of the two other files that it needs from
34  * the patch.
35  *
36  * Wiggle performs one of three core function:
37  *   --extract -x    extract part of a patch or merge file
38  *   --diff -d       report differences between two files
39  *   --merge -m      merge the changes between two files into a third file
40  *
41  * This is also a --browse (-B) mode which provides interactive access
42  * to the merger.
43  *
44  * To perform these, wiggle requires 1, 2, or 3 input streams respectively.
45  * I can get these from individual files, from a diff (unified or context) or
46  * from a merge file.
47  *
48  * For merge:
49  *    If one file is given, it is a merge file (output of 'merge').
50  *    If two files are given, the second is assumed to be a patch,
51  *         the first is a normal file.
52  *    If three files are given, they are taken to be normal files.
53  *
54  * For diff:
55  *    If one file is given, it is a patch
56  *    If two files are given, they are normal files.
57  *
58  * For extract:
59  *    Only one file can be given. -p indicates it is a patch,
60  *        otherwise it is a merge.
61  *    One of the flags -1 -2 or -3 must also be given and they indicate which
62  *    part of the patch or merge to extract.
63  *
64  * Difference calculation and merging is performed on lines (-l) or words (-w).
65  * Each 'word' is either 1/all alphnumeric (or '_'), 2/ all space or tab,
66  * or 3/ any other single character.
67  *
68  * In the case of -w, an initial diff is computed based on non-trivial words
69  * which includes alhpanumeric words and newlines.
70  *
71  * This diff is computed from the ends of the file and is used to find
72  * a suitable starting point and range.  Then a more precise diff is
73  * computed over that restricted range
74  *
75  * Other options available are:
76  *   --replace -r   replace first file with  result of merge.
77  *   --help -h      provide help
78  *   --version -v   version
79  *
80  * Defaults are --merge --words
81  *
82  */
83 #define _GNU_SOURCE
84 #include        "wiggle.h"
85 #include        <errno.h>
86 #include        <fcntl.h>
87 #include        <unistd.h>
88 #include        <stdlib.h>
89 #include        <stdio.h>
90 #include        <ctype.h>
91 #include        <sys/stat.h>
92
93 static void printsep(struct elmnt e1, struct elmnt e2)
94 {
95         int a, b, c, d, e, f;
96         sscanf(e1.start+1, "%d %d %d", &a, &b, &c);
97         sscanf(e2.start+1, "%d %d %d", &d, &e, &f);
98         printf("@@ -%d,%d +%d,%d @@%s", b, c, e, f, e1.start+18);
99 }
100
101 static int extract(int argc, char *argv[], int ispatch, int which)
102 {
103         /* extract a branch of a diff or diff3 or merge output
104          * We need one file
105          */
106         struct stream f, flist[3];
107
108         if (argc == 0) {
109                 fprintf(stderr,
110                         "%s: no file given for --extract\n", wiggle_Cmd);
111                 return 2;
112         }
113         if (argc > 1) {
114                 fprintf(stderr,
115                         "%s: only give one file for --extract\n", wiggle_Cmd);
116                 return 2;
117         }
118         f = wiggle_load_file(argv[0]);
119         if (f.body == NULL) {
120                 fprintf(stderr,
121                         "%s: cannot load file '%s' - %s\n", wiggle_Cmd,
122                         argv[0], strerror(errno));
123                 return 2;
124         }
125         if (ispatch) {
126                 if (wiggle_split_patch(f, &flist[0], &flist[1]) == 0) {
127                         fprintf(stderr,
128                                 "%s: No chunk found in patch: %s\n", wiggle_Cmd,
129                                 argv[0]);
130                         return 0;
131                 }
132         } else {
133                 if (!wiggle_split_merge(f, &flist[0], &flist[1], &flist[2])) {
134                         fprintf(stderr,
135                                 "%s: merge file %s looks bad.\n", wiggle_Cmd,
136                                 argv[0]);
137                         return 2;
138                 }
139         }
140         if (flist[which-'1'].body == NULL) {
141                 fprintf(stderr,
142                         "%s: %s has no -%c component.\n", wiggle_Cmd,
143                         argv[0], which);
144                 return 2;
145         } else {
146                 if (write(1, flist[which-'1'].body,
147                           flist[which-'1'].len)
148                     != flist[which-'1'].len)
149                         return 2;
150         }
151         return 0;
152 }
153
154 static int do_diff_lines(struct file fl[2], struct csl *csl)
155 {
156         int a, b;
157         int exit_status = 0;
158         a = b = 0;
159         while (a < fl[0].elcnt || b < fl[1].elcnt) {
160                 if (a < csl->a) {
161                         if (fl[0].list[a].start[0]) {
162                                 printf("-");
163                                 wiggle_printword(stdout,
164                                                  fl[0].list[a]);
165                         }
166                         a++;
167                         exit_status = 1;
168                 } else if (b < csl->b) {
169                         if (fl[1].list[b].start[0]) {
170                                 printf("+");
171                                 wiggle_printword(stdout,
172                                                  fl[1].list[b]);
173                         }
174                         b++;
175                         exit_status = 1;
176                 } else {
177                         if (fl[0].list[a].start[0] == '\0')
178                                 printsep(fl[0].list[a],
179                                          fl[1].list[b]);
180                         else {
181                                 printf(" ");
182                                 wiggle_printword(stdout,
183                                                  fl[0].list[a]);
184                         }
185                         a++;
186                         b++;
187                         if (a >= csl->a+csl->len)
188                                 csl++;
189                 }
190         }
191         return exit_status;
192 }
193
194 static int do_diff_words(struct file fl[2], struct csl *csl)
195 {
196         int a, b;
197         int exit_status = 0;
198         int sol = 1; /* start of line */
199         a = b = 0;
200         while (a < fl[0].elcnt || b < fl[1].elcnt) {
201                 if (a < csl->a) {
202                         exit_status = 1;
203                         if (sol) {
204                                 int a1;
205                                 /* If we remove a
206                                  * whole line, output
207                                  * +line else clear
208                                  * sol and retry */
209                                 sol = 0;
210                                 for (a1 = a; a1 < csl->a ; a1++)
211                                         if (ends_line(fl[0].list[a1])) {
212                                                 sol = 1;
213                                                 break;
214                                         }
215                                 if (sol) {
216                                         printf("-");
217                                         for (; a < csl->a ; a++) {
218                                                 wiggle_printword(stdout, fl[0].list[a]);
219                                                 if (ends_line(fl[0].list[a])) {
220                                                         a++;
221                                                         break;
222                                                 }
223                                         }
224                                 } else
225                                         printf("|");
226                         }
227                         if (!sol) {
228                                 printf("<<<--");
229                                 do {
230                                         if (sol)
231                                                 printf("|");
232                                         wiggle_printword(stdout, fl[0].list[a]);
233                                         sol = ends_line(fl[0].list[a]);
234                                         a++;
235                                 } while (a < csl->a);
236                                 printf("%s-->>>", sol ? "|" : "");
237                                 sol = 0;
238                         }
239                 } else if (b < csl->b) {
240                         exit_status = 1;
241                         if (sol) {
242                                 int b1;
243                                 sol = 0;
244                                 for (b1 = b; b1 < csl->b; b1++)
245                                         if (ends_line(fl[1].list[b1])) {
246                                                 sol = 1;
247                                                 break;
248                                         }
249                                 if (sol) {
250                                         printf("+");
251                                         for (; b < csl->b ; b++) {
252                                                 wiggle_printword(stdout, fl[1].list[b]);
253                                                 if (ends_line(fl[1].list[b])) {
254                                                         b++;
255                                                         break;
256                                                 }
257                                         }
258                                 } else
259                                         printf("|");
260                         }
261                         if (!sol) {
262                                 printf("<<<++");
263                                 do {
264                                         if (sol)
265                                                 printf("|");
266                                         wiggle_printword(stdout, fl[1].list[b]);
267                                         sol = ends_line(fl[1].list[b]);
268                                         b++;
269                                 } while (b < csl->b);
270                                 printf("%s++>>>", sol ? "|" : "");
271                                 sol = 0;
272                         }
273                 } else {
274                         if (sol) {
275                                 int a1;
276                                 sol = 0;
277                                 for (a1 = a; a1 < csl->a+csl->len; a1++)
278                                         if (ends_line(fl[0].list[a1]))
279                                                 sol = 1;
280                                 if (sol) {
281                                         if (fl[0].list[a].start[0]) {
282                                                 printf(" ");
283                                                 for (; a < csl->a+csl->len; a++, b++) {
284                                                         wiggle_printword(stdout, fl[0].list[a]);
285                                                         if (ends_line(fl[0].list[a])) {
286                                                                 a++, b++;
287                                                                 break;
288                                                         }
289                                                 }
290                                         } else {
291                                                 printsep(fl[0].list[a], fl[1].list[b]);
292                                                 a++; b++;
293                                         }
294                                 } else
295                                         printf("|");
296                         }
297                         if (!sol) {
298                                 wiggle_printword(stdout, fl[0].list[a]);
299                                 if (ends_line(fl[0].list[a]))
300                                         sol = 1;
301                                 a++;
302                                 b++;
303                         }
304                         if (a >= csl->a+csl->len)
305                                 csl++;
306                 }
307         }
308         return exit_status;
309 }
310
311 static int do_diff(int argc, char *argv[], int obj, int ispatch,
312                    int which, int reverse, int shortest)
313 {
314         /* create a diff (line or char) of two streams */
315         struct stream f, flist[3];
316         int chunks1 = 0, chunks2 = 0, chunks3 = 0;
317         int exit_status = 0;
318         struct file fl[2];
319         struct csl *csl;
320
321         switch (argc) {
322         case 0:
323                 fprintf(stderr, "%s: no file given for --diff\n", wiggle_Cmd);
324                 return 2;
325         case 1:
326                 f = wiggle_load_file(argv[0]);
327                 if (f.body == NULL) {
328                         fprintf(stderr,
329                                 "%s: cannot load file '%s' - %s\n", wiggle_Cmd,
330                                 argv[0], strerror(errno));
331                         return 2;
332                 }
333                 chunks1 = chunks2 =
334                         wiggle_split_patch(f, &flist[0], &flist[1]);
335                 if (!flist[0].body || !flist[1].body) {
336                         fprintf(stderr,
337                                 "%s: couldn't parse patch %s\n", wiggle_Cmd,
338                                 argv[0]);
339                         return 2;
340                 }
341                 break;
342         case 2:
343                 flist[0] = wiggle_load_file(argv[0]);
344                 if (flist[0].body == NULL) {
345                         fprintf(stderr,
346                                 "%s: cannot load file '%s' - %s\n", wiggle_Cmd,
347                                 argv[0], strerror(errno));
348                         return 2;
349                 }
350                 if (ispatch) {
351                         f = wiggle_load_file(argv[1]);
352                         if (f.body == NULL) {
353                                 fprintf(stderr,
354                                         "%s: cannot load patch '%s' - %s\n", wiggle_Cmd,
355                                         argv[1], strerror(errno));
356                                 return 2;
357                         }
358                         if (which == '2')
359                                 chunks2 = chunks3 =
360                                         wiggle_split_patch(f, &flist[2],
361                                                            &flist[1]);
362                         else
363                                 chunks2 = chunks3 =
364                                         wiggle_split_patch(f, &flist[1],
365                                                            &flist[2]);
366
367                 } else
368                         flist[1] = wiggle_load_file(argv[1]);
369                 if (flist[1].body == NULL) {
370                         fprintf(stderr,
371                                 "%s: cannot load file '%s' - %s\n", wiggle_Cmd,
372                                 argv[1], strerror(errno));
373                         return 2;
374                 }
375                 break;
376         default:
377                 fprintf(stderr,
378                         "%s: too many files given for --diff\n", wiggle_Cmd);
379                 return 2;
380         }
381         if (reverse) {
382                 f = flist[0];
383                 flist[0] = flist[1];
384                 flist[1] = f;
385         }
386         fl[0] = wiggle_split_stream(flist[0], obj);
387         fl[1] = wiggle_split_stream(flist[1], obj);
388         if (!(obj & WholeWord) && fl[0].elcnt > 50000 && fl[1].elcnt > 50000) {
389                 /* Too big - use fewer words if possible */
390                 free(fl[0].list);
391                 free(fl[1].list);
392                 obj |= WholeWord;
393                 fl[0] = wiggle_split_stream(flist[0], obj);
394                 fl[1] = wiggle_split_stream(flist[1], obj);
395         }
396         if (chunks2 && !chunks1)
397                 csl = wiggle_pdiff(fl[0], fl[1], chunks2);
398         else
399                 csl = wiggle_diff_patch(fl[0], fl[1], shortest);
400         if ((obj & ByMask) == ByLine) {
401                 if (!chunks1)
402                         printf("@@ -1,%d +1,%d @@\n",
403                                fl[0].elcnt, fl[1].elcnt);
404                 exit_status = do_diff_lines(fl, csl);
405         } else {
406                 if (!chunks1) {
407                         /* count lines in each file */
408                         int l1, l2, i;
409                         l1 = l2 = 0;
410                         for (i = 0 ; i < fl[0].elcnt ; i++)
411                                 if (ends_line(fl[0].list[i]))
412                                         l1++;
413                         for (i = 0 ; i < fl[1].elcnt ; i++)
414                                 if (ends_line(fl[1].list[i]))
415                                         l2++;
416                         printf("@@ -1,%d +1,%d @@\n", l1, l2);
417                 }
418                 exit_status = do_diff_words(fl, csl);
419         }
420         return exit_status;
421 }
422
423 static int do_merge(int argc, char *argv[], int obj, int blanks,
424                     int reverse, int replace, char *outfilename,
425                     int ignore, int show_wiggles,
426                     int quiet, int shortest)
427 {
428         /* merge three files, A B C, so changed between B and C get made to A
429          */
430         struct stream f, flist[3];
431         struct file fl[3];
432         int i;
433         int chunks1 = 0, chunks2 = 0, chunks3 = 0;
434         char *replacename = NULL, *orignew = NULL;
435         struct csl *csl1, *csl2;
436         struct ci ci;
437         FILE *outfile = stdout;
438
439         switch (argc) {
440         case 0:
441                 fprintf(stderr, "%s: no files given for --merge\n", wiggle_Cmd);
442                 return 2;
443         case 3:
444         case 2:
445         case 1:
446                 for (i = 0; i < argc; i++) {
447                         flist[i] = wiggle_load_file(argv[i]);
448                         if (flist[i].body == NULL) {
449                                 fprintf(stderr, "%s: cannot load file '%s' - %s\n",
450                                         wiggle_Cmd,
451                                         argv[i], strerror(errno));
452                                 return 2;
453                         }
454                 }
455                 break;
456         default:
457                 fprintf(stderr, "%s: too many files given for --merge\n",
458                         wiggle_Cmd);
459                 return 2;
460         }
461         switch (argc) {
462         case 1: /* a merge file */
463                 f = flist[0];
464                 if (!wiggle_split_merge(f, &flist[0], &flist[1], &flist[2])) {
465                         fprintf(stderr, "%s: merge file %s looks bad.\n",
466                                 wiggle_Cmd,
467                                 argv[0]);
468                         return 2;
469                 }
470                 break;
471         case 2: /* a file and a patch */
472                 f = flist[1];
473                 chunks2 = chunks3 = wiggle_split_patch(f, &flist[1], &flist[2]);
474                 break;
475         case 3: /* three separate files */
476                 break;
477         }
478         if (reverse) {
479                 f = flist[1];
480                 flist[1] = flist[2];
481                 flist[2] = f;
482         }
483
484         for (i = 0; i < 3; i++) {
485                 if (flist[i].body == NULL) {
486                         fprintf(stderr, "%s: file %d missing\n", wiggle_Cmd, i);
487                         return 2;
488                 }
489         }
490         if (outfilename) {
491                 outfile = fopen(outfilename, "w");
492                 if (!outfile) {
493                         fprintf(stderr, "%s: could not create %s\n",
494                                 wiggle_Cmd, outfilename);
495                         return 2;
496                 }
497         } else if (replace) {
498                 int fd;
499                 replacename = wiggle_xmalloc(strlen(argv[0]) + 20);
500                 orignew = wiggle_xmalloc(strlen(argv[0]) + 20);
501                 strcpy(replacename, argv[0]);
502                 strcpy(orignew, argv[0]);
503                 strcat(orignew, ".porig");
504                 if (open(orignew, O_RDONLY) >= 0 ||
505                     errno != ENOENT) {
506                         fprintf(stderr, "%s: %s already exists\n",
507                                 wiggle_Cmd,
508                                 orignew);
509                         free(replacename);
510                         free(orignew);
511                         return 2;
512                 }
513                 strcat(replacename, "XXXXXX");
514                 fd = mkstemp(replacename);
515                 if (fd == -1) {
516                         fprintf(stderr,
517                                 "%s: could not create temporary file for %s\n",
518                                 wiggle_Cmd,
519                                 replacename);
520                         free(replacename);
521                         free(orignew);
522                         return 2;
523                 }
524                 outfile = fdopen(fd, "w");
525         }
526
527         if (obj == 'l')
528                 blanks |= ByLine;
529         else
530                 blanks |= ByWord;
531         fl[0] = wiggle_split_stream(flist[0], blanks);
532         fl[1] = wiggle_split_stream(flist[1], blanks);
533         fl[2] = wiggle_split_stream(flist[2], blanks);
534         if (!(blanks & WholeWord) &&
535             fl[1].elcnt > 50000 &&
536             (fl[0].elcnt > 50000 || fl[2].elcnt > 50000)) {
537                 /* Too many words */
538                 free(fl[0].list);
539                 free(fl[1].list);
540                 free(fl[2].list);
541                 blanks |= WholeWord;
542                 fl[0] = wiggle_split_stream(flist[0], blanks);
543                 fl[1] = wiggle_split_stream(flist[1], blanks);
544                 fl[2] = wiggle_split_stream(flist[2], blanks);
545         }
546
547         if (chunks2 && !chunks1)
548                 csl1 = wiggle_pdiff(fl[0], fl[1], chunks2);
549         else
550                 csl1 = wiggle_diff(fl[0], fl[1], shortest);
551         csl2 = wiggle_diff_patch(fl[1], fl[2], shortest);
552
553         ci = wiggle_make_merger(fl[0], fl[1], fl[2], csl1, csl2,
554                          obj == 'w', ignore, show_wiggles > 1);
555         wiggle_print_merge(outfile, &fl[0], &fl[1], &fl[2],
556                     obj == 'w', ci.merger, NULL, 0, 0);
557         if (!quiet && ci.conflicts)
558                 fprintf(stderr,
559                         "%d unresolved conflict%s found\n",
560                         ci.conflicts,
561                         ci.conflicts == 1 ? "" : "s");
562         if (!quiet && ci.ignored)
563                 fprintf(stderr,
564                         "%d already-applied change%s ignored\n",
565                         ci.ignored,
566                         ci.ignored == 1 ? "" : "s");
567
568         if (outfilename)
569                 fclose(outfile);
570         else if (replace) {
571                 struct stat statbuf;
572
573                 if (stat(argv[0], &statbuf) != 0) {
574                         fprintf(stderr,
575                                 "%s: failed to stat original file. - %s\n",
576                                 wiggle_Cmd, strerror(errno));
577                         free(replacename);
578                         free(orignew);
579                         return 2;
580                 }
581                 if (fchmod(fileno(outfile), statbuf.st_mode) != 0) {
582                         fprintf(stderr,
583                                 "%s: failed to change permission of new file. - %s\n",
584                                 wiggle_Cmd, strerror(errno));
585                         free(replacename);
586                         free(orignew);
587                         return 2;
588                 }
589                 fclose(outfile);
590                 if (rename(argv[0], orignew) == 0 &&
591                     rename(replacename, argv[0]) == 0)
592                         /* all ok */;
593                 else {
594                         fprintf(stderr,
595                                 "%s: failed to move new file into place.\n",
596                                 wiggle_Cmd);
597                         free(replacename);
598                         free(orignew);
599                         return 2;
600                 }
601         }
602         free(replacename);
603         free(orignew);
604         if (show_wiggles)
605                 return ci.conflicts + ci.wiggles > 0;
606         else
607                 return ci.conflicts > 0;
608 }
609
610 static int multi_merge(int argc, char *argv[], int obj, int blanks,
611                        int reverse, int ignore, int show_wiggles,
612                        int replace, int strip,
613                        int quiet, int shortest)
614 {
615         FILE *f;
616         char *filename;
617         struct plist *pl;
618         int num_patches;
619         int rv = 0;
620         int i;
621
622         if (!replace) {
623                 fprintf(stderr,
624                         "%s: -p in merge mode requires -r\n",
625                         wiggle_Cmd);
626                 return 2;
627         }
628         if (argc != 1) {
629                 fprintf(stderr,
630                         "%s: -p in merge mode requires exactly one file\n",
631                         wiggle_Cmd);
632                 return 2;
633         }
634         filename = argv[0];
635         f = fopen(filename, "r");
636         if (!f) {
637                 fprintf(stderr, "%s: cannot open %s\n",
638                         wiggle_Cmd, filename);
639                 return 2;
640         }
641         wiggle_check_dir(filename, fileno(f));
642         pl = wiggle_parse_patch(f, NULL, &num_patches);
643         fclose(f);
644         if (wiggle_set_prefix(pl, num_patches, strip) == 0) {
645                 fprintf(stderr, "%s: aborting\n", wiggle_Cmd);
646                 return 2;
647         }
648         for (i = 0; i < num_patches; i++) {
649                 char *name;
650                 char *av[2];
651                 asprintf(&name, "_wiggle_:%d:%d:%s",
652                          pl[i].start, pl[i].end, filename);
653                 av[0] = pl[i].file;
654                 av[1] = name;
655                 rv |= do_merge(2, av, obj, blanks, reverse, 1, NULL, ignore,
656                                show_wiggles, quiet, shortest);
657         }
658         return rv;
659 }
660
661 int main(int argc, char *argv[])
662 {
663         int opt;
664         int option_index;
665         int mode = 0;
666         int obj = 0;
667         int replace = 0;
668         int backup = 1;
669         int which = 0;
670         int ispatch = 0;
671         int reverse = 0;
672         int verbose = 0, quiet = 0;
673         int strip = -1;
674         int exit_status = 0;
675         int ignore = 1;
676         int shortest = 0;
677         int show_wiggles = 0;
678         char *helpmsg;
679         char *trace;
680         char *outfile = NULL;
681         int selftest = 0;
682         int ignore_blanks = 0;
683
684         trace = getenv("WIGGLE_TRACE");
685         if (trace && *trace)
686                 wiggle_do_trace = 1;
687
688         while ((opt = getopt_long(argc, argv,
689                                   short_options, long_options,
690                                   &option_index)) != -1)
691                 switch (opt) {
692                 case 'h':
693                         helpmsg = Help;
694                         switch (mode) {
695                         case 'x':
696                                 helpmsg = HelpExtract;
697                                 break;
698                         case 'd':
699                                 helpmsg = HelpDiff;
700                                 break;
701                         case 'm':
702                                 helpmsg = HelpMerge;
703                                 break;
704                         case 'B':
705                                 helpmsg = HelpBrowse;
706                                 break;
707                         }
708                         fputs(helpmsg, stderr);
709                         exit(0);
710
711                 case 'V':
712                         fputs(Version, stderr);
713                         exit(0);
714                 case ':':
715                 case '?':
716                 default:
717                         fputs(Usage, stderr);
718                         exit(2);
719
720                 case 'B':
721                 case 'x':
722                 case 'd':
723                 case 'm':
724                         if (mode == 0) {
725                                 mode = opt;
726                                 continue;
727                         }
728                         if (mode == 'B' && opt == 'd') {
729                                 /* Browse/diff mode */
730                                 ispatch = 2;
731                                 continue;
732                         }
733                         fprintf(stderr,
734                                 "%s: mode is '%c' - cannot set to '%c'\n",
735                                 wiggle_Cmd, mode, opt);
736                         exit(2);
737
738                 case NON_SPACE:
739                         ignore_blanks |= WholeWord;
740                         continue;
741
742                 case SHORTEST:
743                         shortest = 1;
744                         continue;
745
746                 case 'w':
747                 case 'l':
748                         if (obj == 0 || obj == opt) {
749                                 obj = opt;
750                                 continue;
751                         }
752                         fprintf(stderr,
753                                 "%s: cannot select both words and lines.\n", wiggle_Cmd);
754                         exit(2);
755
756                 case 'r':
757                         replace = 1;
758                         continue;
759                 case NO_BACKUP:
760                         backup = 0;
761                         continue;
762                 case 'o':
763                         outfile = optarg;
764                         replace = 1;
765                         continue;
766                 case 'R':
767                         reverse = 1;
768                         continue;
769
770                 case 'b':
771                         ignore_blanks |= IgnoreBlanks;
772                         continue;
773
774                 case 'i':
775                         ignore = 0;
776                         continue;
777                 case 'W':
778                         show_wiggles = 2;
779                         ignore = 0;
780                         continue;
781                 case REPORT_WIGGLES:
782                         show_wiggles = 1;
783                         continue;
784
785                 case '1':
786                 case '2':
787                 case '3':
788                         if (which == 0 || which == opt) {
789                                 which = opt;
790                                 continue;
791                         }
792                         fprintf(stderr,
793                                 "%s: can only select one of -1, -2, -3\n", wiggle_Cmd);
794                         exit(2);
795
796                 case 'p': /* 'patch' or 'strip' */
797                         if (optarg)
798                                 strip = atol(optarg);
799                         ispatch = 1;
800                         continue;
801
802                 case 'v':
803                         verbose++;
804                         continue;
805                 case 'q':
806                         quiet = 1;
807                         continue;
808
809                 case SELF_TEST:
810                         selftest = 1;
811                         continue;
812                 }
813         if (!mode)
814                 mode = 'm';
815
816         if (mode == 'B') {
817                 vpatch(argc-optind, argv+optind, ispatch,
818                        strip, reverse, replace, outfile, selftest,
819                        ignore_blanks, backup);
820                 /* should not return */
821                 exit(1);
822         }
823
824         if (obj && mode == 'x') {
825                 fprintf(stderr,
826                         "%s: cannot specify --line or --word with --extract\n",
827                         wiggle_Cmd);
828                 exit(2);
829         }
830         if (mode != 'm' && !obj)
831                 obj = 'w';
832         if (ispatch && outfile) {
833                 fprintf(stderr, "%s: --output incompatible with --patch\n",
834                         wiggle_Cmd);
835                 exit(2);
836         }
837         if (replace && mode != 'm') {
838                 fprintf(stderr,
839                         "%s: --replace or --output only allowed with --merge\n", wiggle_Cmd);
840                 exit(2);
841         }
842         if (mode == 'x' && !which) {
843                 fprintf(stderr,
844                         "%s: must specify -1, -2 or -3 with --extract\n", wiggle_Cmd);
845                 exit(2);
846         }
847         if (mode != 'x' && mode != 'd' && which) {
848                 fprintf(stderr,
849                         "%s: -1, -2 or -3 only allowed with --extract or --diff\n",
850                         wiggle_Cmd);
851                 exit(2);
852         }
853
854         if (ispatch && which == '3') {
855                 fprintf(stderr,
856                         "%s: cannot extract -3 from a patch.\n", wiggle_Cmd);
857                 exit(2);
858         }
859
860         switch (mode) {
861         case 'x':
862                 exit_status = extract(argc-optind, argv+optind, ispatch, which);
863                 break;
864         case 'd':
865                 exit_status = do_diff(argc-optind, argv+optind,
866                                       (obj == 'l' ? ByLine : ByWord)
867                                       | ignore_blanks,
868                                       ispatch, which, reverse, shortest);
869                 break;
870         case 'm':
871                 if (ispatch)
872                         exit_status = multi_merge(argc-optind,
873                                                   argv+optind, obj,
874                                                   ignore_blanks,
875                                                   reverse, ignore,
876                                                   show_wiggles,
877                                                   replace, strip,
878                                                   quiet, shortest);
879                 else
880                         exit_status = do_merge(
881                                 argc-optind, argv+optind,
882                                 obj, ignore_blanks, reverse, replace,
883                                 outfile,
884                                 ignore, show_wiggles, quiet, shortest);
885                 break;
886         }
887         exit(exit_status);
888 }